﻿// *****************************************************************************************************
//   Copyright (c) 2020, HP Development Company, L.P. All rights reserved.
//   This software contains confidential and proprietary information of HP.
//   The user of this software agrees not to disclose, disseminate or copy
//   such Confidential Information and shall use the software only in accordance
//   with the terms of the license agreement the user entered into with HP. 
// *****************************************************************************************************

namespace LedControl.HidControl
{
    using System;
    using System.Runtime.InteropServices;

    /// <summary>
    /// HID Device helper
    /// </summary>
    internal class HidHelper
    {
        #region Fields

        /// <summary>CreateFile : file share for read</summary>
        public const uint FileShareRead = 0x1;

        /// <summary>CreateFile : file share for write</summary>
        public const uint FileShareWrite = 0x2;

        /// <summary>CreateFile : Open file for read</summary>
        public const uint GenericRead = 0x80000000;

        /// <summary>CreateFile : Open file for write</summary>
        public const uint GenericWrite = 0x40000000;

        /// <summary>Used in SetupDiClassDevs to get device interface details</summary>
        private const int DigcfDeviceInterface = 0x10;

        /// <summary>Used in SetupDiClassDevs to get devices present in the system</summary>
        private const int DigcfPresent = 0x02;

        /// <summary>CreateFile : Open handle for overlapped operations</summary>
        private const uint FileFlagOverlapped = 0x40000000;

        /// <summary>CreateFile : Resource to be "created" must exist</summary>
        private const uint OpenExisting = 3;

        /// <summary>
        /// HID Capabilities of opened device
        /// </summary>
        private HidCaps openCapabilities = new HidCaps();

        /// <summary>
        /// Opened HID Handle
        /// </summary>
        private IntPtr openedHidHandle;

        private ushort inputVendorID;
        private ushort inputProductID;
        private ushort inputUsagePage;
        private ushort inputUsage;
        private uint inputAccess;
        private uint inputShareMode;
        private bool inputIsCreateFileOverlapped;

        #endregion

        public bool IsExist
        {
            get
            {
                if (openedHidHandle.ToInt32() == 0 || openedHidHandle.ToInt32() == -1)
                {
                    return false;
                }
                else
                {
                    return true;
                }
            }
        }

        /// <summary>
        /// Ctor of HidHelper, find the specified device to open the handle
        /// </summary>
        /// <param name="vendorID">Vendor ID</param>
        /// <param name="productID">Product ID</param>
        /// <param name="usagePage">Usage Page</param>
        /// <param name="usage">Usage</param>
        /// <param name="access">Access</param>
        /// <param name="shareMode">Share mode</param>
        /// <param name="isCreateFileOverlapped">Need to CreateFile with Overlapped for ReadData</param>
        public HidHelper(ushort vendorID, ushort productID, ushort usagePage, ushort usage, uint access, uint shareMode, bool isCreateFileOverlapped = false)
        {
            inputVendorID = vendorID;
            inputProductID = productID;
            inputUsagePage = usagePage;
            inputUsage = usage;
            inputAccess = access;
            inputShareMode = shareMode;
            inputIsCreateFileOverlapped = isCreateFileOverlapped;
        }

        /// <summary>
        /// Find the device and open the handle
        /// </summary>
        public void Initilize()
        {
            Console.WriteLine("HidHelper_Initilize");
            openedHidHandle = IntPtr.Zero;
            openCapabilities = new HidCaps();

            IntPtr detailData;
            bool result;
            IntPtr devInfo, deviceHandle, returnHIDHandle;
            HidCaps returnCapabilities = new HidCaps();
            HIDD_ATTRIBUTES attributes = new HIDD_ATTRIBUTES();
            int memberIndex = 0;

            Guid hidGuid = new Guid("{0x4D1E55B2, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30}}");

            detailData = IntPtr.Zero;
            deviceHandle = IntPtr.Zero;
            returnHIDHandle = IntPtr.Zero;

            devInfo = SetupDiGetClassDevs(hidGuid, null, IntPtr.Zero, DigcfPresent | DigcfDeviceInterface);
            DeviceInterfaceData devInfoData = new DeviceInterfaceData();
            devInfoData.Size = Marshal.SizeOf(devInfoData);

            memberIndex = 0;

            do
            {
                result = SetupDiEnumDeviceInterfaces(devInfo, IntPtr.Zero, hidGuid, memberIndex, ref devInfoData);

                if (result == true)
                {
                    string strDevicePath = GetDevicePath(devInfo, ref devInfoData);
                    uint creationFlag = 0;
                    if (inputIsCreateFileOverlapped)
                    {
                        creationFlag = FileFlagOverlapped;
                    }

                    deviceHandle = CreateFile(strDevicePath, inputAccess, inputShareMode, IntPtr.Zero, OpenExisting, creationFlag, IntPtr.Zero);

                    //  INVALID_HANDLE_VALUE
                    if (deviceHandle.ToInt32() != -1)
                    {
                        attributes.Size = (ushort)Marshal.SizeOf(attributes);
                        result = HidD_GetAttributes(deviceHandle, out attributes);

                        if (result == true)
                        {
                            GetDeviceCapabilities(deviceHandle, ref returnCapabilities);
                            if (inputVendorID == attributes.VendorID &&
                                inputProductID == attributes.ProductID)
                            {
                                if (inputUsagePage == returnCapabilities.UsagePage &&
                                    inputUsage == returnCapabilities.Usage)
                                {
                                    openedHidHandle = deviceHandle;
                                    openCapabilities = returnCapabilities;
                                    Console.WriteLine($"Opened HID Handle({openedHidHandle}) for VendorID({inputVendorID.ToString("X4")}), ProductID({inputProductID.ToString("X4")}), UsagePage({inputUsagePage.ToString("X4")}), Usage({inputUsage.ToString("X4")}), nAccess ({inputAccess.ToString("X4")}), nShareMode({inputShareMode.ToString("X4")}), IsCreateFileOverlapped({inputIsCreateFileOverlapped})");
                                    return;
                                }
                            }
                        }
                        CloseHandle(deviceHandle);
                    }
                }

                memberIndex = memberIndex + 1;
            }
            while (result == true);

            //Free the memory reserved for hDevInfo by SetupDiClassDevs.
            SetupDiDestroyDeviceInfoList(devInfo);
            return;
        }

        /// <summary>
        /// Finalizes an instance of the <see cref="HidHelper"/> class. 
        /// </summary>
        ~HidHelper()
        {
            CloseHandle(openedHidHandle);
        }

        public bool SetFeature(byte reportId, byte controlCode, byte controlData)
        {
            if (openedHidHandle.ToInt32() == 0 || openedHidHandle.ToInt32() == -1)
            {
                return false;
            }
            byte[] buffer = new byte[openCapabilities.FeatureReportByteLength];
            buffer[0] = reportId;
            buffer[1] = controlCode;
            buffer[2] = controlData;
            bool result = HidD_SetFeature(openedHidHandle, buffer, Convert.ToUInt16(openCapabilities.FeatureReportByteLength));
            if (!result)
            {
                Console.WriteLine("HidD_SetFeature failed error code " + Marshal.GetLastWin32Error());
            }
            return result;
        }

        /// <summary>
        /// HidD_GetFeature on openHidHandle
        /// </summary>
        /// <param name="reportId">Report ID, default 0 is auto find in openCapabilities</param>
        /// <returns>Data from HidD_GetFeature</returns>
        public byte[] GetFeature(byte reportId = 0)
        {
            if (openedHidHandle.ToInt32() == 0 || openedHidHandle.ToInt32() == -1)
            {
                return null;
            }

            byte[] buffer = new byte[openCapabilities.FeatureReportByteLength];

            // Get report id
            if (reportId == 0)
            {
                int calsCapsLength;
                IntPtr preparsedData = IntPtr.Zero;
                HIDP_REPORT_TYPE myType = HIDP_REPORT_TYPE.HidP_Feature;

                if (openCapabilities.NumberOutputButtonCaps != 0)
                {
                    HidP_Button_Caps[] myHIDP_Button_CAPS = new HidP_Button_Caps[openCapabilities.NumberFeatureButtonCaps];
                    calsCapsLength = openCapabilities.NumberFeatureButtonCaps;
                    HidD_GetPreparsedData(openedHidHandle, ref preparsedData);
                    HidP_GetButtonCaps(myType, myHIDP_Button_CAPS, ref calsCapsLength, preparsedData);
                    buffer[0] = myHIDP_Button_CAPS[0].ReportID;
                }
                else
                {
                    HIDP_VALUE_CAPS[] myHIDP_VALUE_CAPS = new HIDP_VALUE_CAPS[openCapabilities.NumberFeatureValueCaps];
                    calsCapsLength = openCapabilities.NumberFeatureValueCaps;
                    HidD_GetPreparsedData(openedHidHandle, ref preparsedData);
                    HidP_GetValueCaps(myType, myHIDP_VALUE_CAPS, ref calsCapsLength, preparsedData);
                    buffer[0] = myHIDP_VALUE_CAPS[0].ReportID;
                }
            }
            else
            {
                buffer[0] = reportId;
            }

            for (int i = 1; i < openCapabilities.FeatureReportByteLength; i++)
            {
                buffer[i] = 0;
            }

            if (HidD_GetFeature(openedHidHandle, buffer, (uint)openCapabilities.FeatureReportByteLength))
            {
                return buffer;
            }
            else
            {
                return null;
            }
        }

        #region Native enum
        /// <summary>
        /// The HIDP_REPORT_TYPE enumeration type is used to specify a HID report type.
        /// </summary>
        private enum HIDP_REPORT_TYPE : int
        {
            /// <summary>
            /// Indicates an input report.
            /// </summary>
            HidP_Input = 0,

            /// <summary>
            /// Indicates an output report.
            /// </summary>
            HidP_Output = 1,

            /// <summary>
            /// Indicates a feature report.
            /// </summary>
            HidP_Feature = 2
        }
        #endregion

        #region Native method
        [DllImport("hid.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool HidD_SetFeature(IntPtr HidDeviceObject, ref byte[] Buffer, uint BufferLength);

        [DllImport("HID.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool HidD_SetFeature(IntPtr hidDeviceObject, byte[] reportBuffer, uint reportBufferLength);

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern int CloseHandle(IntPtr file);

        [DllImport("kernel32", SetLastError = true)]
        private static extern IntPtr CreateEvent(IntPtr eventAttributes, bool manualReset, bool initialState, [MarshalAs(UnmanagedType.LPStr)] string name);

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern IntPtr CreateFile([MarshalAs(UnmanagedType.LPStr)] string name, uint access, uint shareMode, IntPtr security, uint creationFlags, uint attributes, IntPtr template);

        [DllImport("HID.dll", CharSet = CharSet.Auto)]
        private static extern bool HidD_GetAttributes(IntPtr hidDeviceObject, out HIDD_ATTRIBUTES attributes);

        [DllImport("HID.dll", CharSet = CharSet.Auto)]
        private static extern bool HidD_GetFeature(IntPtr hidDeviceObject, [Out] byte[] reportBuffer, uint reportBufferLength);

        [DllImport("HID.dll", CharSet = CharSet.Auto)]
        private static extern void HidD_GetPreparsedData(IntPtr hidDeviceObject, ref IntPtr preparsedData);

        [DllImport("Kernel32.dll")]
        private static extern bool ReadFile(IntPtr hndRef, [Out] byte[] buffer, int numberOfBytesToRead, out int numberOfBytesRead, ref OVERLAPPED flag);

        [DllImport("kernel32", SetLastError = true)]
        private static extern bool ResetEvent(IntPtr eventHandle);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool GetOverlappedResult(IntPtr hFile,
            [In] ref OVERLAPPED lpOverlapped,
            out uint lpNumberOfBytesTransferred, bool bWait);

        [DllImport("setupapi.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool SetupDiEnumDeviceInterfaces(IntPtr deviceInfoSet, IntPtr deviceInfoData, [MarshalAs(UnmanagedType.LPStruct)] Guid interfaceClassGuid, int memberIndex, ref DeviceInterfaceData deviceInterfaceData);

        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        private static extern IntPtr SetupDiGetClassDevs([MarshalAs(UnmanagedType.LPStruct)] Guid classGuid, string enumerator, IntPtr hwndParent, int flags);

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern int WaitForSingleObject(IntPtr handle, uint milliseconds);

        [DllImport("kernel32.dll")]
        private static extern bool WriteFile(IntPtr file, byte[] buffer, int numberOfBytesToWrite, out int numberOfBytesWritten, IntPtr overlapped);

        [DllImport("setupapi.dll", SetLastError = true)]
        private static extern int SetupDiDestroyDeviceInfoList(IntPtr infoSet);

        [DllImport("setupapi.dll", SetLastError = true)]
        private static extern bool SetupDiEnumDeviceInterfaces(IntPtr deviceInfoSet, uint deviceInfoData, ref Guid guidClass, uint index, ref DeviceInterfaceData interfaceData);

        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr deviceInfoSet, ref DeviceInterfaceData interfaceData, IntPtr deviceInterfaceDetailData, uint deviceInterfaceDetailDataSize, ref uint requiredSize, IntPtr deviceInfoData);

        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr deviceInfoSet, ref DeviceInterfaceData interfaceData, ref DeviceInterfaceDetailData detailData, uint deviceInterfaceDetailDataSize, ref uint requiredSize, IntPtr deviceInfoData);

        [DllImport("HID.dll", CharSet = CharSet.Auto)]
        private static extern bool HidD_FreePreparsedData(ref IntPtr preparsedData);

        [DllImport("kernel32.dll", EntryPoint = "SetLastError")]
        private static extern void SetLastError(int errorCode);

        [DllImport("HID.dll", CharSet = CharSet.Auto)]
        private static extern void HidD_GetHidGuid(Guid hidGuid);

        [DllImport("hid.dll", SetLastError = true)]
        private static unsafe extern int HidD_GetPreparsedData(int hidDeviceObject, ref int preparsedData);

        [DllImport("HID.dll", CharSet = CharSet.Auto)]
        private static extern int HidP_GetButtonCaps(HIDP_REPORT_TYPE reportType, [In, Out] HidP_Button_Caps[] buttonCaps, ref int buttonCapsLength, IntPtr preparsedData);

        [DllImport("HID.dll", CharSet = CharSet.Auto)]
        private static extern int HidP_GetCaps(IntPtr preparsedData, out HidCaps capabilities);

        [DllImport("hid.dll", SetLastError = true)]
        private static unsafe extern int HidP_GetValueCaps(HIDP_REPORT_TYPE reportType, [In, Out] HIDP_VALUE_CAPS[] valueCaps, ref int valueCapsLength, IntPtr preparsedData);

        /// <summary>
        /// Get device path
        /// </summary>
        /// <param name="infoSet">device information set</param>
        /// <param name="deviceInterface">device interface</param>
        /// <returns>Device path</returns>
        private static string GetDevicePath(IntPtr infoSet, ref DeviceInterfaceData deviceInterface)
        {
            uint requiredSize = 0;

            // Get the device interface details
            if (!SetupDiGetDeviceInterfaceDetail(infoSet, ref deviceInterface, IntPtr.Zero, 0, ref requiredSize, IntPtr.Zero))
            {
                DeviceInterfaceDetailData detail = new DeviceInterfaceDetailData();

                if (IntPtr.Size == 8)
                {
                    detail.Size = 8; // for 64 bit operating systems
                }
                else
                {
                    detail.Size = 4 + (uint)Marshal.SystemDefaultCharSize; // for 32 bit systems
                }

                if (SetupDiGetDeviceInterfaceDetail(infoSet, ref deviceInterface, ref detail, requiredSize, ref requiredSize, IntPtr.Zero))
                {
                    return detail.DevicePath;
                }
            }

            return null;
        }

        /// <summary>
        /// Get device capabilities
        /// </summary>
        /// <param name="deviceHandle">device hangle</param>
        /// <param name="capabilities">capabilities</param>
        private void GetDeviceCapabilities(IntPtr deviceHandle, ref HidCaps capabilities)
        {
            IntPtr preparsedData2 = IntPtr.Zero;

            HidD_GetPreparsedData(deviceHandle, ref preparsedData2);
            HidP_GetCaps(preparsedData2, out capabilities);
            HidD_FreePreparsedData(ref preparsedData2);
        }

        #endregion

        #region Native struct

        /// <summary>
        /// An SP_DEVICE_INTERFACE_DATA structure defines a device interface in a device information set.
        /// </summary>
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        private struct DeviceInterfaceData
        {
            /// <summary>
            /// The size, in bytes, of the SP_DEVICE_INTERFACE_DATA structure. For more information, see the Remarks section.
            /// </summary>
            public int Size;

            /// <summary>
            /// The GUID for the class to which the device interface belongs.
            /// </summary>
            public Guid InterfaceClassGuid;

            /// <summary>
            /// Can be one or more of the following: SPINT_ACTIVE, SPINT_DEFAULT, SPINT_REMOVED
            /// </summary>
            public int Flags;

            /// <summary>
            /// Reserved. Do not use.
            /// </summary>
            public IntPtr Reserved;
        }

        /// <summary>
        /// An SP_DEVICE_INTERFACE_DETAIL_DATA structure contains the path for a device interface.
        /// </summary>
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        private struct DeviceInterfaceDetailData
        {
            /// <summary>
            /// The size, in bytes, of the SP_DEVICE_INTERFACE_DETAIL_DATA structure. For more information, see the following Remarks section.
            /// </summary>
            public uint Size;

            /// <summary>
            /// A NULL-terminated string that contains the device interface path. This path can be passed to Win32 functions such as CreateFile.
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
            public string DevicePath;
        }

        /// <summary>
        /// The HIDP_CAPS structure contains information about a top-level collection's capability.
        /// </summary>
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        private struct HidCaps
        {
            /// <summary>
            /// Specifies a top-level collection's usage ID.
            /// </summary>
            public ushort Usage;

            /// <summary>
            /// Specifies the top-level collection's usage page.
            /// </summary>
            public ushort UsagePage;

            /// <summary>
            /// Specifies the maximum size, in bytes, of all the input reports (including the report ID, if report IDs are used, which is prepended to the report data).
            /// </summary>
            public short InputReportByteLength;

            /// <summary>
            /// Specifies the maximum size, in bytes, of all the output reports (including the report ID, if report IDs are used, which is prepended to the report data).
            /// </summary>
            public short OutputReportByteLength;

            /// <summary>
            /// Specifies the maximum length, in bytes, of all the feature reports (including the report ID, if report IDs are used, which is prepended to the report data).
            /// </summary>
            public short FeatureReportByteLength;

            /// <summary>
            /// Reserved for internal system use.
            /// </summary>
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)]
            public short[] Reserved;

            /// <summary>
            /// Specifies the number of HIDP_LINK_COLLECTION_NODE structures that are returned for this top-level collection by HidP_GetLinkCollectionNodes.
            /// </summary>
            public short NumberLinkCollectionNodes;

            /// <summary>
            /// Specifies the number of input HIDP_BUTTON_CAPS structures that HidP_GetButtonCaps returns.
            /// </summary>
            public short NumberInputButtonCaps;

            /// <summary>
            /// Specifies the number of input HIDP_VALUE_CAPS structures that HidP_GetValueCaps returns.
            /// </summary>
            public short NumberInputValueCaps;

            /// <summary>
            /// Specifies the number of data indices assigned to buttons and values in all input reports.
            /// </summary>
            public short NumberInputDataIndices;

            /// <summary>
            /// Specifies the number of output HIDP_BUTTON_CAPS structures that HidP_GetButtonCaps returns.
            /// </summary>
            public short NumberOutputButtonCaps;

            /// <summary>
            /// Specifies the number of output HIDP_VALUE_CAPS structures that HidP_GetValueCaps returns.
            /// </summary>
            public short NumberOutputValueCaps;

            /// <summary>
            /// Specifies the number of data indices assigned to buttons and values in all output reports.
            /// </summary>
            public short NumberOutputDataIndices;

            /// <summary>
            /// Specifies the total number of feature HIDP_BUTTONS_CAPS structures that HidP_GetButtonCaps returns.
            /// </summary>
            public short NumberFeatureButtonCaps;

            /// <summary>
            /// Specifies the total number of feature HIDP_VALUE_CAPS structures that HidP_GetValueCaps returns.
            /// </summary>
            public short NumberFeatureValueCaps;

            /// <summary>
            /// Specifies the number of data indices assigned to buttons and values in all feature reports.
            /// </summary>
            public short NumberFeatureDataIndices;
        }

        /// <summary>
        /// The HIDD_ATTRIBUTES structure contains vendor information about a HIDClass device.
        /// </summary>
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        private struct HIDD_ATTRIBUTES
        {
            /// <summary>
            /// Specifies the size, in bytes, of a HIDD_ATTRIBUTES structure.
            /// </summary>
            public int Size;

            /// <summary>
            /// Specifies a HID device's vendor ID.
            /// </summary>
            public ushort VendorID;

            /// <summary>
            /// Specifies a HID device's product ID.
            /// </summary>
            public ushort ProductID;

            /// <summary>
            /// Specifies the manufacturer's revision number for a HIDClass device.
            /// </summary>
            public ushort VersionNumber;
        }

        /// <summary>
        /// The HIDP_BUTTON_CAPS structure contains information about the capability of a HID control button usage (or a set of buttons associated with a usage range).
        /// </summary>
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        private struct HidP_Button_Caps
        {
            /// <summary>
            /// Specifies the usage page for a usage or usage range.
            /// </summary>
            public short UsagePage;

            /// <summary>
            /// Specifies the report ID of the HID report that contains the usage or usage range.
            /// </summary>
            public byte ReportID;

            /// <summary>
            /// Indicates, if TRUE, that a button has a set of aliased usages. Otherwise, if IsAlias is FALSE, the button has only one usage.
            /// </summary>
            public byte IsAlias;

            /// <summary>
            /// Contains the data fields (one or two bytes) associated with an input, output, or feature main item.
            /// </summary>
            public ushort BitField;

            /// <summary>
            /// Specifies the index of the link collection in a top-level collection's link collection array that contains the usage or usage range. If LinkCollection is zero, the usage or usage range is contained in the top-level collection.
            /// </summary>
            public ushort LinkCollection;

            /// <summary>
            /// Specifies the usage of the link collection that contains the usage or usage range. If LinkCollection is zero, LinkUsage specifies the usage of the top-level collection.
            /// </summary>
            public ushort LinkUsage;

            /// <summary>
            /// Specifies the usage page of the link collection that contains the usage or usage range. If LinkCollection is zero, LinkUsagePage specifies the usage page of the top-level collection.
            /// </summary>
            public ushort LinkUsagePage;

            /// <summary>
            /// Specifies, if TRUE, that the structure describes a usage range. Otherwise, if IsRange is FALSE, the structure describes a single usage.
            /// </summary>
            public byte IsRange;

            /// <summary>
            /// Specifies, if TRUE, that the usage or usage range has a set of string descriptors. Otherwise, if IsStringRange is FALSE, the usage or usage range has zero or one string descriptor.
            /// </summary>
            public byte IsStringRange;

            /// <summary>
            /// Specifies, if TRUE, that the usage or usage range has a set of designators. Otherwise, if IsDesignatorRange is FALSE, the usage or usage range has zero or one designator.
            /// </summary>
            public byte IsDesignatorRange;

            /// <summary>
            /// Specifies, if TRUE, that the button usage or usage range provides absolute data. Otherwise, if IsAbsolute is FALSE, the button data is the change in state from the previous value.
            /// </summary>
            public byte IsAbsolute;

            /// <summary>
            /// Reserved for internal system use.
            /// </summary>
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
            public int[] Reserved;

            /// <summary>
            /// Indicates the inclusive lower bound of usage range whose inclusive upper bound is specified by Range.UsageMax.
            /// </summary>
            public short UsageMin;

            /// <summary>
            /// Indicates the inclusive upper bound of a usage range whose inclusive lower bound is indicated by Range.UsageMin.
            /// </summary>
            public short UsageMax;

            /// <summary>
            /// Indicates the inclusive lower bound of a range of string descriptors (specified by string minimum and string maximum items) whose inclusive upper bound is indicated by Range.StringMax.
            /// </summary>
            public ushort StringMin;

            /// <summary>
            /// Indicates the inclusive upper bound of a range of string descriptors (specified by string minimum and string maximum items) whose inclusive lower bound is indicated by Range.StringMin.
            /// </summary>
            public ushort StringMax;

            /// <summary>
            /// Indicates the inclusive lower bound of a range of designators (specified by designator minimum and designator maximum items) whose inclusive lower bound is indicated by Range.DesignatorMax.
            /// </summary>
            public ushort DesignatorMin;

            /// <summary>
            /// Indicates the inclusive upper bound of a range of designators (specified by designator minimum and designator maximum items) whose inclusive lower bound is indicated by Range.DesignatorMin.
            /// </summary>
            public ushort DesignatorMax;

            /// <summary>
            /// Indicates the inclusive lower bound of a sequential range of data indices that correspond, one-to-one and in the same order, to the usages specified by the usage range Range.UsageMin to Range.UsageMax.
            /// </summary>
            public ushort DataIndexMin;

            /// <summary>
            /// Indicates the inclusive upper bound of a sequential range of data indices that correspond, one-to-one and in the same order, to the usages specified by the usage range Range.UsageMin to Range.UsageMax.
            /// </summary>
            public ushort DataIndexMax;
        }

        /// <summary>
        /// The HIDP_VALUE_CAPS structure contains information that describes the capability of a set of HID control values (either a single usage or a usage range).
        /// </summary>
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        private struct HIDP_VALUE_CAPS
        {
            /// <summary>
            /// Specifies the usage page of the usage or usage range.
            /// </summary>
            public short UsagePage;

            /// <summary>
            /// Specifies the report ID of the HID report that contains the usage or usage range.
            /// </summary>
            public byte ReportID;

            /// <summary>
            /// Indicates, if TRUE, that the usage is member of a set of aliased usages. Otherwise, if IsAlias is FALSE, the value has only one usage.
            /// </summary>
            public byte IsAlias;

            /// <summary>
            /// Contains the data fields (one or two bytes) associated with an input, output, or feature main item.
            /// </summary>
            public ushort BitField;

            /// <summary>
            /// Specifies the index of the link collection in a top-level collection's link collection array that contains the usage or usage range. If LinkCollection is zero, the usage or usage range is contained in the top-level collection.
            /// </summary>
            public ushort LinkCollection;

            /// <summary>
            /// Specifies the usage of the link collection that contains the usage or usage range. If LinkCollection is zero, LinkUsage specifies the usage of the top-level collection.
            /// </summary>
            public short LinkUsage;

            /// <summary>
            /// Specifies the usage page of the link collection that contains the usage or usage range. If LinkCollection is zero, LinkUsagePage specifies the usage page of the top-level collection.
            /// </summary>
            public short LinkUsagePage;

            /// <summary>
            /// Specifies, if TRUE, that the structure describes a usage range. Otherwise, if IsRange is FALSE, the structure describes a single usage.
            /// </summary>
            public byte IsRange;

            /// <summary>
            /// Specifies, if TRUE, that the usage or usage range has a set of string descriptors. Otherwise, if IsStringRange is FALSE, the usage or usage range has zero or one string descriptor.
            /// </summary>
            public byte IsStringRange;

            /// <summary>
            /// Specifies, if TRUE, that the usage or usage range has a set of designators. Otherwise, if IsDesignatorRange is FALSE, the usage or usage range has zero or one designator.
            /// </summary>
            public byte IsDesignatorRange;

            /// <summary>
            /// Specifies, if TRUE, that the usage or usage range provides absolute data. Otherwise, if IsAbsolute is FALSE, the value is the change in state from the previous value.
            /// </summary>
            public byte IsAbsolute;

            /// <summary>
            /// Specifies, if TRUE, that the usage supports a NULL value, which indicates that the data is not valid and should be ignored. Otherwise, if HasNull is FALSE, the usage does not have a NULL value.
            /// </summary>
            public byte HasNull;

            /// <summary>
            /// Reserved for internal system use.
            /// </summary>
            public byte Reserved;

            /// <summary>
            /// Specifies the size, in bits, of a usage's data field in a report. If ReportCount is greater than one, each usage has a separate data field of this size.
            /// </summary>
            public short BitSize;

            /// <summary>
            /// Specifies the number of usages that this structure describes.
            /// </summary>
            public short ReportCount;

            /// <summary>
            /// Reserved for internal system use.
            /// </summary>
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
            public short[] Reserved2;

            /// <summary>
            /// Specifies the usage's exponent, as described by the USB HID standard.
            /// </summary>
            public int UnitsExp;

            /// <summary>
            /// Specifies the usage's units, as described by the USB HID Standard.
            /// </summary>
            public int Units;

            /// <summary>
            /// Specifies a usage's signed lower bound.
            /// </summary>
            public int LogicalMin;

            /// <summary>
            /// Specifies a usage's signed upper bound.
            /// </summary>
            public int LogicalMax;

            /// <summary>
            /// Specifies a usage's signed lower bound after scaling is applied to the logical minimum value.
            /// </summary>
            public int PhysicalMin;

            /// <summary>
            /// Specifies a usage's signed upper bound after scaling is applied to the logical maximum value.
            /// </summary>
            public int PhysicalMax;

            /// <summary>
            /// Indicates the inclusive lower bound of usage range whose inclusive upper bound is specified by Range.UsageMax.
            /// </summary>
            public short UsageMin;

            /// <summary>
            /// Indicates the inclusive upper bound of a usage range whose inclusive lower bound is indicated by Range.UsageMin.
            /// </summary>
            public short UsageMax;

            /// <summary>
            /// Indicates the inclusive lower bound of a range of string descriptors (specified by string minimum and string maximum items) whose inclusive upper bound is indicated by Range.StringMax.
            /// </summary>
            public short StringMin;

            /// <summary>
            /// Indicates the inclusive upper bound of a range of string descriptors (specified by string minimum and string maximum items) whose inclusive lower bound is indicated by Range.StringMin.
            /// </summary>
            public short StringMax;

            /// <summary>
            /// Indicates the inclusive lower bound of a range of designators (specified by designator minimum and designator maximum items) whose inclusive lower bound is indicated by Range.DesignatorMax.
            /// </summary>
            public short DesignatorMin;

            /// <summary>
            /// Indicates the inclusive upper bound of a range of designators (specified by designator minimum and designator maximum items) whose inclusive lower bound is indicated by Range.DesignatorMin.
            /// </summary>
            public short DesignatorMax;

            /// <summary>
            /// Indicates the inclusive lower bound of a sequential range of data indices that correspond, one-to-one and in the same order, to the usages specified by the usage range Range.UsageMin to Range.UsageMax.
            /// </summary>
            public short DataIndexMin;

            /// <summary>
            /// Indicates the inclusive upper bound of a sequential range of data indices that correspond, one-to-one and in the same order, to the usages specified by the usage range Range.UsageMin to Range.UsageMax.
            /// </summary>
            public short DataIndexMax;
        }

        /// <summary>
        /// Contains information used in asynchronous (or overlapped) input and output (I/O).
        /// </summary>
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        private struct OVERLAPPED
        {
            /// <summary>
            /// The status code for the I/O request. When the request is issued, the system sets this member to STATUS_PENDING to indicate that the operation has not yet started. When the request is completed, the system sets this member to the status code for the completed request.
            /// </summary>
            public UIntPtr Internal;

            /// <summary>
            /// The number of bytes transferred for the I/O request. The system sets this member if the request is completed without errors.
            /// </summary>
            public UIntPtr InternalHigh;

            /// <summary>
            /// The low-order portion of the file position at which to start the I/O request, as specified by the user.
            /// </summary>
            public uint Offset;

            /// <summary>
            /// The high-order portion of the file position at which to start the I/O request, as specified by the user.
            /// </summary>
            public uint OffsetHigh;

            /// <summary>
            /// A handle to the event that will be set to a signaled state by the system when the operation has completed. The user must initialize this member either to zero or a valid event handle using the CreateEvent function before passing this structure to any overlapped functions. This event can then be used to synchronize simultaneous I/O requests for a device. For additional information, see Remarks. Functions such as ReadFile and WriteFile set this handle to the nonsignaled state before they begin an I/O operation.When the operation has completed, the handle is set to the signaled state. Functions such as GetOverlappedResult and the synchronization wait functions reset auto-reset events to the nonsignaled state. Therefore, you should use a manual reset event; if you use an auto-reset event, your application can stop responding if you wait for the operation to complete and then call GetOverlappedResult with the bWait parameter set to TRUE.
            /// </summary>
            public IntPtr EventHandle;
        }
        #endregion
    }
}
